﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Net;
using System.Text.RegularExpressions;
using CrawlerCore;
using HtmlAgilityPack;

namespace WCrawlerLib
{
    public abstract class BrowserSession_IPB : BrowserSession_Forum
    {
        public BrowserSession_IPB()
        {
            ForumTypeId = enumForumType.IPB;
            string boundary = "---------------------------" + DateTime.Now.Ticks.ToString("x");
            ContentType = "multipart/form-data; boundary=" + boundary;
        }

        /// <summary>
        /// Get user id
        /// </summary>
        /// <param name="forumUrl"></param>
        /// <param name="userName"></param>
        /// <param name="doc"></param>
        /// <returns></returns>
        public override int GetUserId(string forumUrl, string userName)
        {
            //*********************************************************************************
            //Get User Id
            //*********************************************************************************
            int userId = -1;
            string homeUrl = forumUrl + "/" + "index.php?";
            HtmlDocument doc = Get(homeUrl);
            string pattern = @"ipb\.vars\[\'member_id\']\s*\t*=\sparseInt\(\s(?<userId>([0-9]+))\s\)\;";
            Regex r = new Regex(pattern);
            Match m = r.Match(doc.DocumentNode.InnerHtml);
            if (m.Success)
                userId = int.Parse(m.Result("${userId}"));
            return userId;
        }

        ///// <summary>
        ///// ipb.vars['member_id']			= parseInt( 2811 );
        ///// </summary>
        ///// <returns></returns>
        //public override int GetUserIdAfterLogin()
        //{
        //    int userId = -1;
        //    string pattern = @"ipb\.vars\[\'member_id\']\s*\t*=\sparseInt\(\s(?<userId>([0-9]+))\s\)\;";
        //    Regex r = new Regex(pattern);
        //    Match m = r.Match(HtmlDoc.DocumentNode.InnerHtml);
        //    if (m.Success)
        //        userId = int.Parse(m.Result("${userId}"));
        //    return userId;
        //}

        ///// <summary>
        ///// Search f value in button New Thread with url template do=newthread&f=[number]
        ///// </summary>
        ///// <param name="threadUrl"></param>
        ///// <returns></returns>
        //public override string FindNewThreadValue(string threadUrl, string pattern)
        //{
        //    string fValue = "";
        //    try
        //    {

        //        Regex r = new Regex(pattern);
        //        Match m = r.Match(threadUrl);
        //        if (m.Success)
        //            fValue = m.Result("${fValue}");
        //    }
        //    catch (WebException we)
        //    {
               

        //    }
        //    catch (Exception ex)
        //    {

        //    }
        //    return fValue;
        //}

        public override string FValue_Pattern()
        {
            return @"do\=new_post\&f=(?<fValue>([0-9]+))";
            ////http://worldwide-forum.net/forum/index.php?showforum=53
            //return @"showforum=(?<fValue>([0-9]+))";
        }

        /// <summary>
        /// Login in a forum. If susscessful set _isLogged = true, else false
        /// </summary>
        public override bool DoLogin(string forumUrl, string loginUrl, string postParams, string username, string password)
        {
            bool loginStatus = false;
            try
            {
                //Init login params
                InitPostParams(enumPost.Login, username, password);
                loginUrl = GeneralClass.GetFullUrl(forumUrl, loginUrl);
                Post(loginUrl);
                string content = HtmlDoc.DocumentNode.InnerHtml.ToLower();
                List<string> key = new List<string>();

                string pattern = @"(thank you|successful(ly|) logged in|log out|" + username + ")";

                Regex r = new Regex(pattern);
                MatchCollection m = r.Matches(content);

                //If count > 1 successfully log in
                if (m.Count >= 1)
                {
                    loginStatus = true;
                }
            }
            catch (WebException we)
            {
                throw we;
                //Console.WriteLine(e.Message);
                //loginStatusId = 2;
            }
            catch (Exception ex)
            {
                throw ex;
                //Console.WriteLine(e.Message);
                //loginStatusId = 2;
            }
            return loginStatus;
        }

        /// <summary>
        /// Get PostId
        /// </summary>
        /// <param name="doc"></param>
        /// <returns></returns>
        public override string[] GetThreadPostNum(HtmlDocument doc)//(string threadUrl)
        {
            //HtmlDocument doc = Get(threadUrl);
            string[] result = new string[2];

            if (doc != null)
            {
                //Find thread num
                ///index.php?app=forums&module=post&section=post&do=reply_post&f=79&t=45717
                ///index.php?app=forums&module=post&section=post&do=reply_post&f=20&t=2056230
                //string xPath = ".//a[contains(@href,'index.php?app=forums&module=post&section=post&do=reply_post')]";
                string xPath = ".//a[contains(@href,'do=reply_post')]";
                HtmlNodeCollection nodeCollection = doc.DocumentNode.SelectNodes(xPath);
                if (nodeCollection != null)
                {
                    string replyUrl = nodeCollection[0].Attributes["href"].Value.Replace("postcount", "");
                    Regex r = new Regex(@"do\=(reply_post|edit_post)(\&|\&amp\;)f=[0-9]+(\&|\&amp\;)t=(?<threadnum>[0-9]+)");
                    Match m = r.Match(replyUrl);
                    if (m.Success)
                    {
                        result[0] = m.Result("${threadnum}");
                    }
                }

                //Find first post num
                xPath = ".//div[starts-with(@id,'post_id_')]";
                nodeCollection = doc.DocumentNode.SelectNodes(xPath);
                if (nodeCollection != null)
                    result[1] = nodeCollection[0].Attributes["id"].Value.Replace("post_id_", "");
            }
            return result;
        }

        ///// <summary>
        ///// Init params of new thread
        ///// </summary>
        ///// <param name="postParams"></param>
        ///// <param name="subject"></param>
        ///// <param name="message"></param>
        ///// <param name="fId"></param>
        ///// <param name="tagList"></param>
        ///// <returns></returns>
        //public override string InitNewThreadParams(string postParams, params string[] pars)//string subject, string message, string fId, string icon, string tagList)
        //{
        //    Dictionary<string, string> paramDict = new Dictionary<string, string>();
        //    //========================================================================
        //    //Init new thread params
        //    paramDict.Add("TopicTitle", pars[0]);
        //    paramDict.Add("TopicDesc", pars[0]);
        //    paramDict.Add("poll_question", "");
        //    paramDict.Add("ed-0_wysiwyg_used", "0");
        //    paramDict.Add("editor_ids[]", "ed-0");
        //    paramDict.Add("Post", pars[1].Replace("&", "%26"));
        //    paramDict.Add("enableemo", "yes");
        //    paramDict.Add("enablesig", "yes");
        //    paramDict.Add("iconid", pars[3]);
        //    paramDict.Add("st", HtmlDoc.GetValueInInputTag("hidden", "name", "st"));
        //    paramDict.Add("app", HtmlDoc.GetValueInInputTag("hidden", "name", "app"));
        //    paramDict.Add("module", HtmlDoc.GetValueInInputTag("hidden", "name", "module"));
        //    paramDict.Add("section", HtmlDoc.GetValueInInputTag("hidden", "name", "section"));
        //    paramDict.Add("do", HtmlDoc.GetValueInInputTag("hidden", "name", "do"));
        //    paramDict.Add("s", HtmlDoc.GetValueInInputTag("hidden", "name", "s"));
        //    paramDict.Add("p", HtmlDoc.GetValueInInputTag("hidden", "name", "p"));
        //    paramDict.Add("t", HtmlDoc.GetValueInInputTag("hidden", "name", "t"));
        //    paramDict.Add("f", pars[2]); //fId
        //    paramDict.Add("parent_id", HtmlDoc.GetValueInInputTag("hidden", "name", "parent_id"));
        //    paramDict.Add("attach_post_key", HtmlDoc.GetValueInInputTag("hidden", "name", "attach_post_key"));
        //    paramDict.Add("removeattachid", HtmlDoc.GetValueInInputTag("hidden", "name", "removeattachid"));
        //    paramDict.Add("dosubmit", HtmlDoc.GetValueInInputTag("submit", "name", "dosubmit"));

        //    string boundary = pars[4];// "---------------------------" + DateTime.Now.Ticks.ToString("x");
        //    string formdataTemplate = "Content-Disposition: form-data; name=\"{0}\"\r\n\r\n{1}";
        //    foreach (KeyValuePair<string, string> par in paramDict)
        //    {
        //        postParams += "\r\n--" + boundary + "\r\n";
        //        postParams += string.Format(formdataTemplate, par.Key, par.Value);
        //    }
        //    postParams += "\r\n--" + boundary + "--\r\n";
        //    return postParams;
        //}

        public string InitPostParams(enumPost postType, string postParams, params string[] pars)
        {
            Dictionary<string, string> paramDict = new Dictionary<string, string>();
            if (postType == enumPost.Login)
            {
                ////Method 1
                //_postParams = "auth_key=" + HtmlDoc.GetValueInInputTag("hidden", "name", "auth_key")
                //        + "&referer=" + pars[2]
                //        + "&username=" + pars[0]
                //        + "&password=" + pars[1]
                //        + "&rememberMe=" + "1";

                ////Method 1
                //_postParams = "auth_key=" + HtmlDoc.GetValueInInputTag("hidden", "name", "auth_key")
                //        + "&referer=" + pars[2]
                //        + "&ips_username=" + pars[0]
                //        + "&ips_password=" + pars[1]
                //        + "&rememberMe=" + "1";

                paramDict.Add("referer", pars[2]);
                paramDict.Add("username", pars[0]);
                paramDict.Add("password", pars[1]);
                paramDict.Add("rememberMe", "1");
                //for ipbb2
                paramDict.Add("ips_username", pars[0]);
                paramDict.Add("ips_password", pars[1]);

            }
            else if (postType == enumPost.NewThread)
            {
                //========================================================================
                //Init new thread params
                paramDict.Add("TopicTitle", pars[0]);
                paramDict.Add("TopicDesc", pars[0]);
                paramDict.Add("poll_question", "");
                paramDict.Add("ed-0_wysiwyg_used", "0");
                paramDict.Add("editor_ids[]", "ed-0");
                paramDict.Add("Post", pars[1].Replace("&", "%26"));
                paramDict.Add("enableemo", "yes");
                paramDict.Add("enablesig", "yes");
                paramDict.Add("iconid", pars[3]);
                paramDict.Add("st", HtmlDoc.GetValueInInputTag("hidden", "name", "st"));
                paramDict.Add("app", HtmlDoc.GetValueInInputTag("hidden", "name", "app"));
                paramDict.Add("module", HtmlDoc.GetValueInInputTag("hidden", "name", "module"));
                paramDict.Add("section", HtmlDoc.GetValueInInputTag("hidden", "name", "section"));
                paramDict.Add("do", HtmlDoc.GetValueInInputTag("hidden", "name", "do"));
                paramDict.Add("s", HtmlDoc.GetValueInInputTag("hidden", "name", "s"));
                paramDict.Add("p", HtmlDoc.GetValueInInputTag("hidden", "name", "p"));
                paramDict.Add("t", HtmlDoc.GetValueInInputTag("hidden", "name", "t"));
                paramDict.Add("f", pars[2]); //fId
                paramDict.Add("parent_id", HtmlDoc.GetValueInInputTag("hidden", "name", "parent_id"));
                paramDict.Add("attach_post_key", HtmlDoc.GetValueInInputTag("hidden", "name", "attach_post_key"));
                paramDict.Add("removeattachid", HtmlDoc.GetValueInInputTag("hidden", "name", "removeattachid"));
                paramDict.Add("dosubmit", HtmlDoc.GetValueInInputTag("submit", "name", "dosubmit"));

                string boundary = pars[4];// "---------------------------" + DateTime.Now.Ticks.ToString("x");
                string formdataTemplate = "Content-Disposition: form-data; name=\"{0}\"\r\n\r\n{1}";
            }
            else if (postType == enumPost.EditPost)
            {

            }
            else if (postType == enumPost.ReplyPost)
            {

            }

            paramDict.Add("auth_key", HtmlDoc.GetValueInInputTag("hidden", "name", "auth_key"));

            foreach (KeyValuePair<string, string> par in paramDict)
            {
                if (postParams.Contains(par.Key))
                    postParams = postParams.Replace(par.Key, par.Value);
            }
            return postParams;
        }

        /// <summary>
        /// Post a new thread
        /// </summary>
        /// <param name="forumUrl"></param>
        /// <param name="newThreadurl"></param>
        /// <param name="postParams"></param>
        /// <param name="subject"></param>
        /// <param name="message"></param>
        /// <param name="fId"></param>
        /// <param name="iconId"></param>
        /// <param name="tagList"></param>
        /// <returns></returns>
        public override void PostNewThread(string forumUrl, string newThreadUrl, string postParams, string subject, string message, string fId, string iconId, string tagList, string prefixId)
        {
            //string postNewThreadUrl = forumUrl + "/" + "newthread.php?do=newthread&f=" + fId.ToString() + "&s=" + SessionHash;
            newThreadUrl = newThreadUrl.Replace("§","&sect").Replace("[fid]", fId.ToString());
            newThreadUrl = GeneralClass.GetFullUrl(forumUrl, newThreadUrl);
            Get(newThreadUrl);
            
            string postNewThreadUrl = forumUrl + "/index.php?";
            //Get POST Params 
            string boundary = "---------------------------" + DateTime.Now.Ticks.ToString("x");

            _postParams = InitPostParams(enumPost.NewThread, postParams, subject, message, fId, iconId, boundary);
            Post(postNewThreadUrl);
        }


        
        //public string GetEditPostParams(HtmlDocument doc, string nameTag, string pattern)
        //{
        //    string value = "";
        //    pattern = @"ipb\.vars\[\'" + nameTag + @"\']\s*\t*=\s" + pattern;//parseInt\(\s(?<value>([0-9]+))\s\)\;";
        //    Regex r = new Regex(pattern);
        //    Match m = r.Match(doc.DocumentNode.InnerHtml);
        //    if (m.Success)
        //        value = m.Result("${value}");
        //    return value;

        //}

        /// <summary>
        /// Init params of new thread
        /// </summary>
        /// <param name="postParams"></param>
        /// <param name="subject"></param>
        /// <param name="message"></param>
        /// <param name="fId"></param>
        /// <param name="tagList"></param>
        /// <returns></returns>
        public virtual string InitEditPostParams(params string[] pars)
        {
            return string.Empty;
        }

        /// <summary>
        /// Update content of a thread have been posted. 
        /// </summary>
        /// <param name="forumUrl"></param>
        /// <param name="topicUrl"></param>
        /// <param name="postId"></param>
        /// <param name="editParams"></param>
        /// <param name="subject"></param>
        /// <param name="message"></param>
        /// <param name="updateReason"></param>
        /// <param name="icon"></param>
        /// <returns></returns>
        public override bool EditPost(string forumUrl, string topicUrl, string postId, string editParams, string subject, string message, string updateReason, string icon)
        {
            bool result = false;

            //Load topic url
            HtmlDocument doc = Get(forumUrl + "/" + topicUrl);

            //Load quick edit
            string quickEditUrl = doc.DocumentNode.SelectNodes(".//a[contains(@id,'edit_post_')]")[0].Attributes["href"].Value;
            Get(quickEditUrl);

            string fId = "";
            Regex  r = new Regex(@"\&f\=(?<fId>[0-9]+)\&");
            Match m = r.Match(quickEditUrl);
            if (m.Success)
                fId = m.Result("${fId}");
            if (string.IsNullOrEmpty(fId))
                return false;

            string boundary = "---------------------------" + DateTime.Now.Ticks.ToString("x");
            //start edit post
            //_postParams = InitPostUpdateParams(editParams, postId, "", message + "\n" + updateReason, updateReason, "");
            _postParams = InitEditPostParams(subject, message + "\n" + updateReason, fId, icon, boundary);
            string updateUrl = forumUrl + "/index.php?";
            
            Post(updateUrl);

            //Get status
            if (HtmlWeb.StatusCode == HttpStatusCode.OK)
                result = true;
            return result;
        }


        /// <summary>
        /// Get group title include titles of subforums 
        /// vBulletin3x: .//table[@id='threadslist']/tbody/tr[./td[contains(@id, 'td_threadtitle')]]
        /// vBulletin4x: .//li[contains(@id,'thread')]/div
        /// </summary>
        /// <param name="forum"></param>
        /// <param name="xPath"></param>
        /// <returns></returns>
        public override DataTable SearchForumThreads(string forumUrl, string statisticUrl, string xPath_Paging, DataTable table, bool isPaging, bool isGetFirstThreads)
        {
            //xPath_Paging = @".//a[contains(@href, '&page=')]";
            table = base.SearchForumThreads(forumUrl, statisticUrl, xPath_Paging, table, isPaging, isGetFirstThreads);
            return table;

            //List<string> urlList = new List<string>();
            //statisticUrl = GeneralClass.GetFullUrl(forumUrl, statisticUrl);
            //Get(statisticUrl);
            ////Search threads in first statistic page
            //List<DataRow> rowList = SearchThreadsByPaging(forumUrl, HtmlDoc, table);
            //string[] result = new string[2];
            //foreach (DataRow row in rowList)
            //{
            //    result = GetThreadPostNum(row["Url"].ToString());
            //    row["ThreadNum"] = result[0];
            //    row["PostNum"] = result[1];
            //    table.Rows.Add(row);
            //    //if only get first thread
            //    if (isGetFirstThreads)
            //        return table;
            //}

            //if (isPaging)
            //{
            //    ////Find url of pages if paging
            //    //string xPath = @".//a[contains(@href, '&page=')]";
            //    //HtmlNodeCollection nodeCollection = HtmlDoc.DocumentNode.SelectNodes(xPath);
            //    //if (nodeCollection == null)
            //    //    return table;

            //    //object lockRow = new object();
            //    //object lockList = new object();
            //    //Parallel.ForEach<HtmlNode>(nodeCollection, node =>
            //    //{
            //    //    statisticUrl = node.Attributes["href"].Value;
            //    //    if (!urlList.Contains(statisticUrl))
            //    //    {
            //    //        lock (lockList)
            //    //        {
            //    //            urlList.Add(statisticUrl);
            //    //        }
            //    //        statisticUrl = GeneralClass.GetFullUrl(forumUrl, statisticUrl);
            //    //        HtmlDocument doc = Get(statisticUrl);
            //    //        List<DataRow> rowList1 = SearchThreadsByPaging(forumUrl, doc, table);
            //    //        foreach (DataRow row in rowList1)
            //    //        {
            //    //            lock (lockRow)
            //    //            {
            //    //                table.Rows.Add(row);
            //    //            }
            //    //        }
            //    //    }
            //    //});
            //}
            //return table;
        }
    }
}